在 shell 中可以运行程序, 而 shell 本身就是一种编程语言. shell 程序, 一般称之为 shell 脚本.

shell 脚本

shell 脚本是一个包含一系列命令的文件. 运行一个脚本就是运行这个文件中 的每个命令.

脚本中除了命令之外还包括以下元素.

  1. 变量 脚本中可以定义变量
  2. 用户输入 read 命令告诉 shell 要从标准输入中读入一个字符串
  3. 控制 包含 if..then..else..fi 控制语句. 还有其他 while/case和for
  4. 环境

smsh1 命令行解析

smsh1 由三个文件组成: smsh1.c, splitline.c 和 excute.c

smsh.h

#include <sys/types.h>
#define YES 1
#define NO 0
/* next_cmd 从输入流中读入下一个命令. 它调用 malloc 来分配内存以接受
 * 任意长度的命令行. 碰到文件结束覆, 它返回 NULL.
 */
char *next_cmd();
/* splitline 将一个字符串分解为字符串数组, 并返回这个数组.
 * 它调 malloc 来分配内存以接受任意参数个数的命令行.
 * 这个数组由 NULL 标记结束.
 */
char **splitline(char *);
void freelist(char **);
void *emalloc(size_t);
void *erealloc(void *, size_t);
/* execute 使用 fork, execvp 和 wait 来运行一个命令.
 * execute 返回命令的结束状态
 */
int execute(char **);
void fatal(char *, char *, int);

smsh1.c

/* smsh1.c small - shell version 1
 * first really usefull version after prompting shell
 * this one parses the command line into strings
 * uses fork, exec, wait, and ignores signals
 */
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include "smsh.h"

#define DFL_PROMPT ">"

int main()
{
    char *cmdline, *prompt, **arglist;
    int result;
    void setup();
    prompt = DFL_PROMPT;
    setup();
    while ((cmdline = next_cmd(prompt, stdin)) != NULL){
        if ((arglist = splitline(cmdline)) != NULL){
            result = execute(arglist);
            freelist(arglist);
        }
        free(cmdline);
    }
    return 0;
}

void setup()
{
    signal(SIGINT, SIG_IGN);
    signal(SIGQUIT, SIG_IGN);
}

void fatal(char *s1, char * s2, int n)
{
    fprintf(stderr, "Error: %s, %s\n", s1, s2);
    exit(n);
}

splitline.c

/* splitline.c - command reading and parsing functions for smsh
 *
 * char *next_cmd(char *prompt, FILE *fp) - get next command
 * char **splitline(char *str); - parse a string
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "smsh.h"

char *next_cmd(char *prompt, FILE *fp)
{
    char *buf;
    int bufspace = 0;
    int pos = 0;
    int c;

    printf("%s", prompt);
    while ((c = getc(fp)) != EOF){
        if (pos + 1 >= bufspace){
            if (bufspace == 0)
                buf = emalloc(BUFSIZ);
            else
                buf = erealloc(buf, bufspace + BUFSIZ);
        bufspace += BUFSIZ;
        }
        if (c == '\n')
            break;
        buf[pos++] = c;
    }
    if (c == EOF && pos == 0)
        return NULL;
    buf[pos] = '\0';
    return buf;
}

#define is_delim(x) ((x) == ' '||(x) == '\t')

char **splitline(char *line)
{
    char *newstr();
    char **args;
    int spots = 0;
    int bufspace = 0;
    int argnum = 0;
    char *cp = line;
    char *start;
    int len;

    if (line = NULL)
        return NULL;
    args = emalloc(BUFSIZ);
    bufspace = BUFSIZ;
    spots = BUFSIZ/sizeof(char *);
    while (*cp != '\0')
    {
        while (is_delim(*cp))
            cp++;
        if(*cp == '\0')
            break;
        if (argnum + 1 >= spots){
            args = erealloc(args, bufspace + BUFSIZ);
            bufspace += BUFSIZ;
            spots += (BUFSIZ / sizeof(char *));
        }
        start = cp;
        len = 1;
        while (*++cp != '\0' && !(is_delim(*cp)))
            len++;
        args[argnum++] = newstr(start, len);
    }
    args[argnum] = NULL;
    return args;
}

char *newstr(char *s, int l)
{
    char *rv = emalloc(l + 1);
    rv[l] = '\0';
    strncpy(rv, s, l);
    return rv;
}

void freelist(char **list)
{
    char **cp = list;
    while (*cp)
        free(*cp++);
    free(list);
}

void *emalloc(size_t n)
{
    void *rv;
    if ((rv = malloc(n)) == NULL)
        fatal("out of memory", "", 1);
    return rv;
}

void *erealloc(void *p, size_t n)
{
    void *rv;
    if ((rv = realloc(p, n)) == NULL)
        fatal("relloc() failed", "", 1);
    return rv;
}

execute.c

/* execute.c - code used by small shell to execute commands */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/wait.h>

int execute(char *argv[])
{
    int pid;
    int child_info = -1;
    if (argv[0] == NULL)
        return 0;
    if ((pid = fork()) == -1)
        perror("fork");
    else if (pid == 0){
        signal(SIGINT, SIG_DFL);
        signal(SIGQUIT, SIG_DFL);
        execvp(argv[0], argv);
        perror("cannot execute command");
        exit(1);
    } else {
        if (wait(&child_info) == -1)
            perror("wait");
    }
    return child_info;
}

使用下面命令编译:

cc smsh1.c splitline.c execute.c -o smsh1